package org.ne.springboot.utils.PaperHandleUtils.CommonUtils;

import java.awt.*;
import java.io.*;
import java.util.*;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.spire.doc.Document;
import com.spire.doc.FileFormat;
import com.spire.doc.Section;
import com.spire.doc.documents.Borders;
import com.spire.doc.documents.Paragraph;
import com.spire.doc.fields.TextRange;
import com.spire.doc.formatting.Border;
//import com.spire.doc.formatting.Borders;
import com.spire.doc.formatting.CharacterFormat;
import com.spire.doc.formatting.ParagraphFormat;
import org.apache.poi.xwpf.usermodel.*;
import org.springframework.web.multipart.MultipartFile;

/**
 * word读取工具类
 *
 * @author Administrator
 */
public class WordUtils {

    /**
     * 从 MultipartFile 中提取Word文档标题
     * @param multipartFile 上传的Word文档文件
     * @return 包含标题文本和层级的Map列表
     * @throws IOException 当文件读取或解析失败时抛出
     */
    public static List<Map<String, Object>> extractTitles(MultipartFile multipartFile) throws IOException {
        // 使用 try-with-resources 确保 InputStream 和 XWPFDocument 被正确关闭
        try (InputStream is = multipartFile.getInputStream(); XWPFDocument doc = new XWPFDocument(is)) {

            List<Map<String, Object>> titles = new ArrayList<>();
            // 获取文档样式表
            XWPFStyles styles = doc.getStyles();

            // 预编译正则表达式，提高效率
            Pattern headingPattern = Pattern.compile("(heading|标题)\\s*(\\d+)", Pattern.CASE_INSENSITIVE);
            Pattern digitalPattern = Pattern.compile("(\\d+)");

            for (XWPFParagraph para : doc.getParagraphs()) {
                String content = para.getText().trim();
//                if (!isContent && content.contains("致") && content.contains("谢")) {
//                    isContent = true;
//                    continue;
//                }
//                if (!isContent) {
//                    continue;
//                }
                String styleID = para.getStyleID();
                String styleName = null;
                Integer level = null;

                // 1. 优先通过样式表解析样式名称
                if (styles != null && styleID != null) {
                    XWPFStyle style = styles.getStyle(styleID);
                    if (style != null) {
                        styleName = style.getName();
                    }
                }

                // 2. 主要逻辑：基于样式名称判断是否为标题并提取层级
                if (styleName != null) {
                    String lowerStyleName = styleName.toLowerCase();
                    Matcher headingMatcher = headingPattern.matcher(lowerStyleName);

                    // 情况A: 样式名明确匹配 "heading {数字}" 或 "标题{数字}"
                    if (headingMatcher.find()) {
                        String numStr = headingMatcher.group(2); // 提取数字部分
                        try {
                            level = Integer.parseInt(numStr);
                        } catch (NumberFormatException e) {
                            // 理论上不会发生，因为正则已匹配数字
                            level = null;
                        }
                    }
                    // 情况B: 样式名可能包含其他形式的数字（如自定义样式"MyStyle-1"）
                    else {
                        Matcher digitalMatcher = digitalPattern.matcher(styleName);
                        if (digitalMatcher.find()) {
                            try {
                                int extractedNum = Integer.parseInt(digitalMatcher.group(1));
                                // 添加额外逻辑判断这个数字是否很可能是层级
                                // 例如，数字在合理范围内 (e.g., 1-9)
                                if (extractedNum >= 1 && extractedNum <= 9) {
                                    level = extractedNum;
                                }
                            } catch (NumberFormatException e) {
                                level = null;
                            }
                        }
                    }
                    // 情况C: 样式名表明是标题但无法解析出具体层级（如"Title"）
                    if (level == null && (lowerStyleName.contains("heading") || lowerStyleName.contains("标题"))) {
                        // 用一个特殊值标记为“未知层级”的标题
                        level = -1;
                    }
                }

                // 3. 确认为标题后，提取文本并添加到结果列表
                if (level != null) {
                    String text = para.getText();
                    if (!text.isEmpty()) {
                        Map<String, Object> titleInfo = new HashMap<>();
                        titleInfo.put("text", text);
                        titleInfo.put("level", level);
                        // 可选：如果需要，也可以把 styleName 和 styleID 放入Map
                        // titleInfo.put("styleName", styleName);
                        // titleInfo.put("styleID", styleID);
                        titles.add(titleInfo);
                    }
                }
            }
            return titles;
        }
        // 注意：try-with-resources 会自动关闭 is 和 doc，这里只需捕获或抛出IOException
    }

    //好像没用
//    public static List<Map<String, Object>> extract1LevelTitles(MultipartFile multipartFile) throws IOException {
//        //List<String>
//        List<Map<String, Object>> titles = extractTitles(multipartFile);
//        List<Map<String, Object>> level_1 = new ArrayList<>();
//
//        for (Map<String, Object> titleInfo : titles) {
//            Integer level = (Integer) titleInfo.get("level");
//            if (level == 1) {
//                level_1.add(titleInfo);
//            }
//        }
//        return level_1;
//    }





    public static List<String> extractTextFromDocx(MultipartFile file) throws IOException {
        List<String> lines = new ArrayList<>();
        try (InputStream inputStream = file.getInputStream();
             XWPFDocument document = new XWPFDocument(inputStream)) {

            for (XWPFParagraph paragraph : document.getParagraphs()) {
                String text = paragraph.getText().trim();
                if (!text.isEmpty()) {
                    lines.add(text);
                }
            }
        }
        return lines;
    }

    /**
     * 提取Word文档中的文本和段落格式信息
     * @param file MultipartFile类型的Word文档
     * @return 包含所有段落格式信息的列表
     */
    public static List<Map<String, Object>> extractFormatInfo(MultipartFile file) {
        List<Map<String, Object>> result = new ArrayList<>();

        try {
            // 创建Document对象
            Document doc = new Document();

            // 根据文件类型加载文档
            String fileName = file.getOriginalFilename();
            String fileType = fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase();

            switch (fileType) {
                case "docx":
                    doc.loadFromStream(file.getInputStream(), FileFormat.Docx);
                    break;
                case "doc":
                    doc.loadFromStream(file.getInputStream(), FileFormat.Doc);
                    break;
                case "wps":
                    doc.loadFromStream(file.getInputStream(), FileFormat.Auto);
                    break;
                default:
                    throw new IllegalArgumentException("不支持的文件格式: " + fileType);
            }

            // 遍历所有章节
            for (int s = 0; s < doc.getSections().getCount(); s++) {
                Section section = doc.getSections().get(s);

                // 遍历所有段落
                for (int p = 0; p < section.getParagraphs().getCount(); p++) {
                    Paragraph paragraph = section.getParagraphs().get(p);
                    Map<String, Object> paraInfo = new HashMap<>();

                    // 提取段落格式信息
                    extractParagraphFormat(paragraph, paraInfo);

                    // 提取段落中的文本格式信息
                    extractTextFormat(paragraph, paraInfo);

                    result.add(paraInfo);
                }
            }

            doc.close();

        } catch (Exception e) {
            throw new RuntimeException("处理Word文档时发生错误: " + e.getMessage(), e);
        }

        return result;
    }

    /**
     * 提取段落格式信息（修正版）
     */
    private static void extractParagraphFormat(Paragraph paragraph, Map<String, Object> paraInfo) {
        ParagraphFormat format = paragraph.getFormat();

        // 设置段落文本内容
        // 段落文本
        paraInfo.put("paragraph_text", paragraph.getText());

        // 提取段落格式属性
        // 对齐方式
        paraInfo.put("alignment", format.getHorizontalAlignment().toString());
        // 左缩进
        paraInfo.put("left_indent", format.getLeftIndent());
        // 右缩进
        paraInfo.put("right_indent", format.getRightIndent());
        // 首行缩进
        paraInfo.put("first_line_indent", format.getFirstLineIndent());
        // 行间距
        paraInfo.put("line_spacing", format.getLineSpacing());
        // 行间距规则
        paraInfo.put("line_spacing_rule", format.getLineSpacingRule().toString());
        // 段后间距
        paraInfo.put("after_spacing", format.getAfterSpacing());
        // 段前间距
        paraInfo.put("before_spacing", format.getBeforeSpacing());

        // 正确提取段落边框信息
        Borders borders = format.getBorders();
        Map<String, Object> borderInfo = new HashMap<>();
        // 获取左边框信息
        Border leftBorder = borders.getLeft();
        // 左边框样式
        borderInfo.put("left_style", leftBorder.getBorderType().toString());
        // 左边框颜色
        borderInfo.put("left_color", colorToHex(leftBorder.getColor()));
        // 左边框宽度
        borderInfo.put("left_width", leftBorder.getLineWidth());

        // 获取右边框信息
        Border rightBorder = borders.getRight();
        // 右边框样式
        borderInfo.put("right_style", rightBorder.getBorderType().toString());
        // 右边框颜色
        borderInfo.put("right_color", colorToHex(rightBorder.getColor()));
        // 右边框宽度
        borderInfo.put("right_width", rightBorder.getLineWidth());

        // 获取上边框信息
        Border topBorder = borders.getTop();
        // 上边框样式
        borderInfo.put("top_style", topBorder.getBorderType().toString());
        // 上边框颜色
        borderInfo.put("top_color", colorToHex(topBorder.getColor()));
        // 上边框宽度
        borderInfo.put("top_width", topBorder.getLineWidth());

        // 获取下边框信息
        Border bottomBorder = borders.getBottom();
        // 下边框样式
        borderInfo.put("bottom_style", bottomBorder.getBorderType().toString());
        // 下边框颜色
        borderInfo.put("bottom_color", colorToHex(bottomBorder.getColor()));
        // 下边框宽度
        borderInfo.put("bottom_width", bottomBorder.getLineWidth());

        // 边框信息
        paraInfo.put("borders", borderInfo);

        // 提取背景色
        Color bgColor = format.getBorders().getColor();
        if (bgColor != null) {
            // 背景颜色
            paraInfo.put("background_color", colorToHex(bgColor));
        }
    }

    /**
     * 将Color对象转换为十六进制颜色字符串
     */
    private static String colorToHex(Color color) {
        if (color == null) {
            return null;
        }
        return String.format("#%02x%02x%02x",
                color.getRed(), color.getGreen(), color.getBlue());
    }

    /**
     * 提取文本格式信息
     */
    private static void extractTextFormat(Paragraph paragraph, Map<String, Object> paraInfo) {
        List<Map<String, Object>> textFormats = new ArrayList<>();

        // 遍历段落中的子对象
        for (int i = 0; i < paragraph.getChildObjects().getCount(); i++) {
            Object child = paragraph.getChildObjects().get(i);

            if (child instanceof TextRange textRange) {
                Map<String, Object> textInfo = new HashMap<>();
                CharacterFormat charFormat = textRange.getCharacterFormat();

                // 设置文本内容
                // 文本内容
                textInfo.put("text", textRange.getText());

                // 提取字符格式属性
                // 字体名称
                textInfo.put("font_name", charFormat.getFontName());
                // 字体大小
                textInfo.put("font_size", charFormat.getFontSize());
                // 是否加粗
                textInfo.put("bold", charFormat.getBold());
                // 是否斜体
                textInfo.put("italic", charFormat.getItalic());
                // 下划线样式
                textInfo.put("underline", charFormat.getUnderlineStyle().toString());

                // 提取文字颜色
                Color textColor = charFormat.getTextColor();
                if (textColor != null) {
                    // 文字颜色
                    textInfo.put("text_color", String.format("#%02x%02x%02x",
                            textColor.getRed(), textColor.getGreen(), textColor.getBlue()));
                }

                // 提取文本背景色（突出显示颜色）
                Color highlightColor = charFormat.getHighlightColor();
                if (highlightColor != null) {
                    // 突出显示颜色
                    textInfo.put("highlight_color", String.format("#%02x%02x%02x",
                            highlightColor.getRed(), highlightColor.getGreen(), highlightColor.getBlue()));
                }

                // 提取文字背景（底纹）
                Color textBgColor = charFormat.getTextBackgroundColor();
                if (textBgColor != null) {
                    // 文字背景颜色
                    textInfo.put("text_background_color", String.format("#%02x%02x%02x",
                            textBgColor.getRed(), textBgColor.getGreen(), textBgColor.getBlue()));
                }

                // 提取其他文本属性
                // 删除线
                textInfo.put("strikethrough", charFormat.getDoubleStrike());
                // 上下标
                textInfo.put("sub_super_script", charFormat.getSubSuperScript().toString());
                // 是否小型大写字母
                textInfo.put("is_small_caps", charFormat.isSmallCaps());
                // 是否全部大写
                textInfo.put("all_caps", charFormat.getAllCaps());

                textFormats.add(textInfo);
            }
        }

        // 文本格式信息
        paraInfo.put("text_formats", textFormats);
    }



/*******************   以下方法作用同上，将形参改为XWPFDocument,获取一级标题返回为List<String>  ********************/
//这是哪个神人写的注释？？


    /**
     * 从 XWPFDocument 中提取Word文档标题
     * @param doc 文档对象
     * @return 标题列表
     */
    private static List<Map<String, Object>> findHeading(XWPFDocument doc) {
        try (doc) {

            List<Map<String, Object>> titles = new ArrayList<>();
            // 获取文档样式表
            XWPFStyles styles = doc.getStyles();

            // 预编译正则表达式，提高效率
            Pattern headingPattern = Pattern.compile("(heading|标题)\\s*(\\d+)", Pattern.CASE_INSENSITIVE);
            Pattern digitalPattern = Pattern.compile("(\\d+)");

            boolean isContent = false;
            for (XWPFParagraph para : doc.getParagraphs()) {
                String content = para.getText().trim();
                if (!isContent && content.contains("致") && content.contains("谢")) {
                    isContent = true;
                    continue;
                }
                if (!isContent) {
                    continue;
                }
                String styleID = para.getStyleID();
                String styleName = null;
                Integer level = null;

                // 1. 优先通过样式表解析样式名称
                if (styles != null && styleID != null) {
                    XWPFStyle style = styles.getStyle(styleID);
                    if (style != null) {
                        styleName = style.getName();
                    }
                }

                // 2. 主要逻辑：基于样式名称判断是否为标题并提取层级
                if (styleName != null) {
                    String lowerStyleName = styleName.toLowerCase();
                    Matcher headingMatcher = headingPattern.matcher(lowerStyleName);

                    // 情况A: 样式名明确匹配 "heading {数字}" 或 "标题{数字}"
                    if (headingMatcher.find()) {
                        // 提取数字部分
                        String numStr = headingMatcher.group(2);
                        try {
                            level = Integer.parseInt(numStr);
                        } catch (NumberFormatException e) {
                            // 理论上不会发生，因为正则已匹配数字
                            level = null;
                        }
                    }
                    // 情况B: 样式名可能包含其他形式的数字（如自定义样式"MyStyle-1"）
                    else {
                        Matcher digitalMatcher = digitalPattern.matcher(styleName);
                        if (digitalMatcher.find()) {
                            try {
                                int extractedNum = Integer.parseInt(digitalMatcher.group(1));
                                // 添加额外逻辑判断这个数字是否很可能是层级
                                // 例如，数字在合理范围内 (e.g., 1-9)
                                if (extractedNum >= 1 && extractedNum <= 9) {
                                    level = extractedNum;
                                }
                            } catch (NumberFormatException e) {
                                level = null;
                            }
                        }
                    }
                    // 情况C: 样式名表明是标题但无法解析出具体层级（如"Title"）
                    if (level == null && (lowerStyleName.contains("heading") || lowerStyleName.contains("标题"))) {
                        // 用一个特殊值标记为“未知层级”的标题
                        level = -1;
                    }
                }

                // 3. 确认为标题后，提取文本并添加到结果列表
                if (level != null) {
                    String text = para.getText().trim();
                    if (!text.isEmpty()) {
                        Map<String, Object> titleInfo = new HashMap<>();
                        titleInfo.put("text", text);
                        titleInfo.put("level", level);
                        // 可选：如果需要，也可以把 styleName 和 styleID 放入Map
                        // titleInfo.put("styleName", styleName);
                        // titleInfo.put("styleID", styleID);
                        titles.add(titleInfo);
                    }
                }
            }
            return titles;
        }catch (IOException e) {
            throw new RuntimeException(e);
        }
        // 注意：try-with-resources 会自动关闭 is 和 doc，这里只需捕获或抛出IOException
    }


    /**
     * 从 XWPFDocument 中提取一级标题
     * @param document 文档对象
     * @return 一级标题列表
     */
    public static List<String> findHeadingOne(XWPFDocument document) throws IOException {
        //List<String>
        List<Map<String, Object>> titles = findHeading(document);
        List<String> level_1 = new ArrayList<>();

        for (Map<String, Object> titleInfo : titles) {
            Integer level = (Integer) titleInfo.get("level");
            if (level == 1) {
                level_1.add((String) titleInfo.get("text"));
            }
        }
        return level_1;
    }

    // 判断字体是否为宋体（SimSun 或 新宋体NSimSun）
    public static boolean  checkFontFamily(XWPFParagraph paragraph) {
        for (XWPFRun run : paragraph.getRuns()) {
            String fontFamily = run.getFontFamily();
            if (fontFamily != null &&
                !("SimSun".equals(fontFamily) ||
                   "NSimSun".equals(fontFamily) ||
                   "宋体".equals(fontFamily) ||
                   "新宋体".equals(fontFamily) ||
                   "Times New Roman".equals(fontFamily)
                )) {
                // 只要有一个run不是宋体或新宋体，就返回false
                return false;
            }
        }
        // 所有run都是宋体或新宋体，或者都没有设置字体（可能继承段落样式）
        return true;
    }

    // 判断字体大小是否为五号（10.5磅）
    public static boolean checkFontSize(XWPFParagraph paragraph) {
        // 五号字对应的磅值
        final double TARGET_SIZE = 10.5;
        // 容差范围
        final double TOLERANCE = 1;

        for (XWPFRun run : paragraph.getRuns()) {
            int fontSize = run.getFontSize();
            if (fontSize != -1) {
                // 将半磅值转换为磅值并比较
                double actualSize = fontSize;
                if (Math.abs(actualSize - TARGET_SIZE) >= TOLERANCE) {
                    return false;
                }
            }
        }
        return true;
    }

    // 判断段落是否居中
    public static boolean checkAlignment(XWPFParagraph paragraph) {
        ParagraphAlignment alignment = paragraph.getAlignment();
        return alignment == ParagraphAlignment.CENTER;
    }
}

